package com.af.security.mgt.account.register;

import com.af.security.mgt.account.register.dto.UserDto;
import com.af.security.mgt.account.register.log.UserRegisterLogService;
import com.af.security.mgt.account.register.token.UserRegisterToken;
import com.af.security.mgt.account.register.token.event.UserRegisterTokenEvent;
import com.af.security.mgt.account.register.token.event.UserRegisterTokenPublisher;
import com.af.security.mgt.user.User;
import com.af.security.mgt.user.UserService;
import com.af.security.property.ProjectProperties;
import com.af.service.AfI18nService;
import com.af.system.util.AfServletUtils;
import com.af.system.api.result.AfResult;
import com.af.system.api.result.AfResultUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @author : zhenyun.su
 * @since : 2019/6/25
 */

@Controller
@RequestMapping("account/register")
public class RegisterController {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private ProjectProperties projectProperties;
    @Autowired
    private RegisterService registerService;
    @Autowired
    private UserService userService;
    @Autowired
    private AfI18nService i18nService;
    @Autowired
    private UserRegisterTokenPublisher tokenPublisher;
    @Autowired
    private UserRegisterLogService userRegisterLogService;

    @GetMapping("create")
    public String createPage(@ModelAttribute("user") UserDto userDto) {
        userDto.setUsername("");
        userDto.setEmail("");
        userDto.setConfirmPass("");
        userDto.setPassword("");
        return "security/account/register/create";
    }

    @PostMapping("create")
    @ResponseBody
    public AfResult<Object> create(UserDto userDto,
                                 @CookieValue(value = "clientLanguage", required = false) String lang,
                                 HttpServletRequest request) throws Exception {
        logger.debug("create account, according to {}", userDto);
        Locale locale = AfServletUtils.getRequestLocale(lang);
        try {
            if (userService.existsByUsername(userDto.getUsername())) {
                return AfResultUtils.failure(i18nService.getI18n("alert.register.username.exists", null, locale), null);
            }
            if (userService.existsByUsername(userDto.getEmail())) {
                return AfResultUtils.failure(i18nService.getI18n("alert.register.mail.exists", null, locale), null);
            }
            if (StringUtils.isEmpty(userDto.getEmail())){
                return AfResultUtils.failure(i18nService.getI18n("alert.mail.required", null, locale), null);
            }
            if (userService.existsByEmail(userDto.getEmail())) {
                return AfResultUtils.failure(i18nService.getI18n("alert.register.mail.exists", null, locale), null);
            }

            if (!StringUtils.isEmpty(userDto.getPhone())) {
                if (userService.existsByPhone(userDto.getPhone())) {
                    return AfResultUtils.failure(i18nService.getI18n("alert.register.phone.exists", null, locale), null);
                }
                String checkInfo = registerService.checkVerificationCode(request);
                if (checkInfo== null){
                    registerService.createNewUser(userDto, true);
                    return AfResultUtils.success("phone");
                }else{
                    return AfResultUtils.failure(i18nService.getI18n(checkInfo, null, locale), null);
                }
            }

            User user = registerService.createNewUser(userDto, false);
            if (user != null) {
                String confirmUrl = projectProperties.getUrl("account/register/confirm?token=");
                tokenPublisher.publishTokenEven(new UserRegisterTokenEvent(confirmUrl, locale, user));
            }
            return AfResultUtils.success("mail");
        } catch (Exception e) {
            return AfResultUtils.failure(i18nService.getI18n("error.register.failure", null, locale), null);
        }

    }

    @GetMapping("success")
    public String successRegister() {
        return "security/account/register/success";
    }
    @GetMapping("success/phone")
    public String successPhoneRegister() {
        return "security/account/register/successPhone";
    }

    @GetMapping("confirm")
    public ModelAndView registerConfirm(@RequestParam("token") String token,
                                        @CookieValue(value = "clientLanguage", required = false) String lang) {
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("status", false);
        map.put("invalidToken", false);
        map.put("expiredToken", false);
        map.put("message", "");
        ModelAndView modelAndView = new ModelAndView("security/account/register/confirm");
        Locale locale = AfServletUtils.getRequestLocale(lang);
        try {
            String errorMessage = null;
            UserRegisterToken userRegisterToken = registerService.findByToken(token);
            if ((userRegisterToken == null) || (!userRegisterToken.getEnabled())) {
                errorMessage = i18nService.getI18n("alert.register.invalidToken", null, locale);
                map.replace("invalidToken", true);
                map.replace("message", errorMessage);
                return modelAndView.addAllObjects(map);
            }
            Duration duration = Duration.between(userRegisterToken.getExpiryDate(), LocalDateTime.now());
            if (duration.toMillis() >= 1) {
                errorMessage = i18nService.getI18n("alert.register.expiredToken", null, locale);
                map.replace("expiredToken", true);
                map.replace("message", errorMessage);
                return modelAndView.addAllObjects(map);
            }
            registerService.activateUser(userRegisterToken);
            map.replace("status", true);
            return modelAndView.addAllObjects(map);
        } catch (Exception e) {
            map.replace("status", false);
            map.replace("message", e.getMessage());
            return modelAndView.addAllObjects(map);
        }
    }

    @GetMapping("resend/Token")
    @ResponseBody
    public AfResult<Object> resendToken(HttpServletRequest request,
                                      @RequestParam("token") String existingToken,
                                      @CookieValue(value = "clientLanguage", required = false) String lang) {
        Locale locale = AfServletUtils.getRequestLocale(lang);
        try {
            UserRegisterToken userRegisterToken = registerService.findByToken(existingToken);
            if (userRegisterToken == null) {
                String errorMessage = i18nService.getI18n("alert.register.invalidToken", null, locale);
                return AfResultUtils.failure(errorMessage, null);
            }
            User user = userService.getById(userRegisterToken.getUserId());
            if (user == null) {
                String errorMessage = i18nService.getI18n("alert.user.not-exist", null, locale);
                return AfResultUtils.failure(errorMessage, null);
            }
            String subject = i18nService.getI18n("register.mail.confirm.Subject", null, locale);
            String message = i18nService.getI18n("register.mail.confirm.resend.Subject", null, locale);
            String appUrl = AfServletUtils.getAppUrl(request, "/account/register/confirm?token=");
            String text = message + " \n " + appUrl;
            registerService.resendTokenMail(user, existingToken, subject, text);
            return AfResultUtils.success(i18nService.getI18n("register.mail.resendToken", null, locale));
        } catch (Exception e) {
            logger.error("resendToken failed; {}", e);
            String error = i18nService.getI18n("error.register.resend", null, locale);
            return AfResultUtils.failure(error, null);
        }
    }

    /**
     * @comment : 手动处理失败， 避免计划调度冲突
     */
    @GetMapping("failure/handle")
    @ResponseBody
    public AfResult<Object> handleUserRegisterfailure() {
        logger.info("handleUserRegisterfailure, start");
        try {
            userRegisterLogService.batchDoingRegisterFailure();
            return AfResultUtils.success();
        } catch (Exception e) {
            logger.error("handleUserRegisterfailure: " + e.getMessage());
            return AfResultUtils.failure("handleUserRegisterfailure", e.getMessage());
        }
    }
}
